#ifndef GX_FR_ENGINE_SCENE_H
#define GX_FR_ENGINE_SCENE_H

#include "Basic/GxLibBasic.h"
#include "LibGL/GxMath3D.h"
#include "Basic/Stream.h"
#include "Entity.h"
#include "Cubemap.h"

namespace FinalRendering
{
	namespace Engine
	{
		using namespace GxLibBasic;

		class IllegalSceneHeaderException
		{};

		struct BBox;

		struct Vertex
		{
			vec3 Position, Normal;
			vec2 TexCoord, LightmapCoord;
		};

		class ConvexHull
		{
		public:
			struct Polygon
			{
				Plane plane;
				Array<vec3> vertices;
				Array<Plane> EdgePlanes;
			};
			Array<RefPtr<Polygon>> Faces;
		};

		struct Surface
		{
			Word VertexCount, MatID, LightmapID, CubemapID;
			UInt * Vertices;
			Surface();
			~Surface();
		};

		struct Portal
		{
			Word Leaf1, Leaf2;
			ConvexHull::Polygon * Polygon;
		};
		
		class Scene;

		//class BspPointEntity : public Object
		//{
		//public:
		//	String Name;
		//	vec3 Position;
		//	virtual int GetTypeID() = 0;
		//	virtual void SaveContent(Stream * stream);
		//	virtual void LoadContent(Stream * stream);
		//	void Save(Stream * stream);
		//	static Entity * LoadEntity(Stream * stream);
		//	virtual void GetOccupiedBspNode(Scene * scene, Array<int> nodes)=0;
		//};

		class LeafInfo : public Object
		{
		public:
			ConvexHull BoundingHull;
			Array<int> Portals;
			Array<IBspEntity*> Entities;
			Array<RefPtr<ConvexHull::Polygon>> SurfacePolygons;
			void SaveToStream(Stream * s);
			void LoadFromStream(Stream * s);
		};

		class BSP_Node
		{
		public:
			union
			{
				UInt ChildBits;
				Word Children[2];
			};
			Word Parent;
			union
			{
				float DividePlane[4];
				struct
				{
					int SurfaceCount;
					int PVS_ID;
					Surface * Surfaces;
					LeafInfo * LeafInfo;
				};
			};
			bool IsLeaf();
			bool HasParent();
		public:
			BSP_Node();
			void Destroy();
		};

		class LightmapInfo
		{
		public:
			vec3 OriPos;
			vec3 AxisX, AxisY; // Pos = OriPos + (coord * AxisX, coord * AxisY)
			vec3 Normal;
			int pixWidth, pixHeight;
		};

		struct Intersection
		{
			bool intersected;
			vec3 point;
			Surface * surf;
		};

		class PotentiallyVisibleSet : public Object
		{
		private:
			static unsigned char masks[8];
			unsigned char * buffer;
			int buflen;
			int count;
			bool GetBit(int i);
			void SetBit(int i, bool val);
		public:
			PotentiallyVisibleSet(int nodeCount);
			~PotentiallyVisibleSet();
			void Set(int i, int j, bool val);
			bool Get(int i, int j);
			void SaveBuffer(Stream * s);
			void LoadBuffer(Stream * s);
			int GetSize();
			void Clear(unsigned char val);
		};

		class CubemapProbe : public Object
		{
		public:
			vec3 Position;
			RefPtr<Cubemap> Cubemap;
		};

		class Scene : public Object
		{
		private:
			void Clear();
			void BspFillParent(int id);
			void GetOccupiedBspNode(Volume & v, Array<int> & nodes);
		public:
			RefPtr<PotentiallyVisibleSet> PVS; 
			vec3 BBoxMin, BBoxMax;
			Array<LightmapInfo, true> Lightmaps;
			Array<String> MatNames;
			Array<Vertex, true> Vertices;
			Array<Portal, true> Portals;
			Array<BSP_Node, true> BSP;
			Array<CubemapProbe> Cubemaps;
			Array<IEntity *> Entities;
			bool IsNegativeNode(int id);
			void SelectLeaf(vec3 & point, int & leafID); // Point
			void SelectLeaf(const vec3 &p1, const vec3 &p2, Array<int> &leaves); // Line Segment
			void SelectLeaf(const BBox & bbox, Array<int> & leaves); // BBox
			void SelectLeaf(const vec3 & point, float radius, Array<int> & leaves); // Sphere
			void ClassifyEntities();
			void Save(const String & fileName);
			void Load(const String & fileName);
			void LineSegIntersection(const vec3 & p1, const vec3 & p2, Intersection & inter, bool getNearest = false);
			Scene();
			~Scene();

		};
	}
}

#endif